const fp = require('lodash/fp');

module.exports = {
    /**
     * 函数柯里化
     * 柯里化可以让我们给一个函数传递较少的参数得到一个已经记住了某些固定参数的 新函数
     * 这是一种对函数参数的“缓存”
     * 让函数变得更加灵活，且粒度更小
     * 可以把多元函数转换成一元函数，可以组合使用函数产生强大的功能
     */
    curry: (func)=> function curriedFn(...args){
        if(args.length < func.length){
            return ()=>curriedFn(...args.concat([...arguments]))
        }
        return  func(...args)
    },
    // Maybe函子
    Maybe: class Maybe {
        static of(value) {
            return new Maybe(value)
        }
        constructor(value) {
            this._value = value
        }
        // 如果对控制变形的话直接返回null的函子
        map(fn) {
            return this.isNothing() ? Maybe.of(null) : Maybe.of(fn(this._value))
        }
        isNothing() {
            return this._value === null || this._value === undefined
        }
    },
    // Functor
    Container: class Container {
        static of(value) {
            return new Container(value)
        }
        constructor(value) {
            this._value = value
        }
        map(fn) {
            return Container.of(fn(this._value))
        }
    },
    // Either函子
    /**
     * Either 两者中的任何一个，类似于if...else...的处理
     * 异常会让函数变得不纯， Either函子可以用来做异常处理
     */
    Left: class Left {
        static of(value){
            return new Left(value)
        }
        constructor (value) {
            this._value = value
        }
        map(fn){
            return this
        }
    },
    Right: class Right {
        static of(value){
            return new Right(value)
        }
        constructor (value) {
            this._value = value
        }
        map(fn){
            return Right.of(fn(this._value))
        }
    },
    // function parseJSON(str) {
    //     try {
    //         return Right.of(JSON.parse(str))
    //     } catch (e) {
    //         return Left.of({error: e.message})
    //     }
    // }

    /**
     * IO函子
     * IO函子中_value是一个函数，这里是把函数作为值来处理
     * IO函子可以把不纯的动作存储到_value中，延迟执行这个不纯的操作(惰性操作)
     * 把不纯的操作交给调用者来处理
     */
    IO: class IO {
        static of(x){
            return new IO(()=>x)
        }
        constructor (fn){
            this._value = fn
        }
        map(fn){
            // 把当前的value和传入的fn组合成一个新的函数
            return new IO(fp.flowRight(fn,this._value))
        }
    },

    /**
     * Monad函子
     * 是可以变扁的Pointed函子，
     * 用于解决函子嵌套的方法，如 IO(IO(x)) //=> 获取值的时候需要  xxx._value()._value()...比较麻烦
     * 一个函子如果具有join和of两个方法，并遵守一些定律就是一个Monad函子
     */
    MonadIO:class MonadIO{
        static of(x){
            return new IO(()=>x)
        }
        constructor (fn){
            this._value = fn
        }
        map(fn){
            // 把当前的value和传入的fn组合成一个新的函数
            return new IO(fp.flowRight(fn,this._value))
        }
        join(){
            return this._value()
        }
        flatMap(fn){
            return  this.map(fn).join()
        }
    }
    // let readFile = filename => new MonadIO(()=> fs.readFileSync(filename,'utf-8'))
    // let print = x=>new MonadIO(()=> x)
    // // 改造前
    // let cat = fp.flowRight(print, readFile)
    // let r = cat('package.json')._value()._value()
    // // 改造后
    // let r = readFile('package.json')
    //             .map(x=>x.toUpperCase())
    //             .flatMap(print)
    //             .join()

}